package com.neusoft.bizcore.file.service.impl;

import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Date;

import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.gridfs.GridFsTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;
import org.springframework.web.multipart.MultipartFile;

import com.mongodb.client.gridfs.GridFSBucket;
import com.mongodb.client.gridfs.model.GridFSFile;
import com.neusoft.bizcore.file.converter.MongoFileConverter;
import com.neusoft.bizcore.file.dto.FileDTO;
import com.neusoft.bizcore.file.model.FileDocument;
import com.neusoft.bizcore.file.service.FileService;
import com.neusoft.bizcore.web.exception.UnitedException;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service
@ConditionalOnClass(MongoTemplate.class)

public class MongoFileServiceImpl implements FileService {
    @Value("${files.storage.mongodb.collection:files}")
    private String collectionName;

    @Autowired
    private MongoTemplate mongoTemplate;
    @Autowired
    private GridFsTemplate gridFsTemplate;
    @Autowired
    private GridFSBucket gridFSBucket;
    @Autowired
    private MongoFileConverter mongoFileConverter;

    @Override
    public FileDTO save(final MultipartFile file) {
        try {
            final String md5 = DigestUtils.md5DigestAsHex(file.getInputStream());
            FileDocument doc = this.getByMd5(md5);
            if (doc != null) {
                return this.mongoFileConverter.toDTO(doc);
            }

            doc = new FileDocument();
            doc.setName(file.getOriginalFilename());
            doc.setSize(file.getSize());
            doc.setContentType(file.getContentType());
            doc.setUploadDate(new Date());
            doc.setMd5(md5);
            final String suffix = file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf(".") + 1);
            doc.setSuffix(suffix);

            final String fsId = this.uploadFileToGridFS(file.getInputStream(), this.createFilePath(suffix), suffix);
            doc.setFsId(fsId);
            doc = this.mongoTemplate.save(doc, this.collectionName);
            return this.mongoFileConverter.toDTO(doc);
        } catch (final IOException e) {
            MongoFileServiceImpl.log.error("", e);
        }
        return null;
    }

    @Override
    public FileDTO getByFsId(final String fsId) {
        final Query query = new Query().addCriteria(Criteria.where("fsId").is(fsId));
        final FileDocument doc = this.mongoTemplate.findOne(query, FileDocument.class, this.collectionName);
        if (null == doc) {
            throw new UnitedException("文件不存在");
        }

        return this.mongoFileConverter.toDTO(doc);
    }

    @Override
    public void download(final String fsId, final HttpServletResponse response) {
        final Query query = new Query().addCriteria(Criteria.where("fsId").is(fsId));
        final FileDocument doc = this.mongoTemplate.findOne(query, FileDocument.class, this.collectionName);
        if (null == doc) {
            throw new UnitedException("文件不存在");
        }
        final Query gridQuery = new Query().addCriteria(Criteria.where("filename").is(doc.getFsId()));
        final GridFSFile fsFile = this.gridFsTemplate.findOne(gridQuery);

        response.setContentType(doc.getContentType());
        try {
            response.setHeader("Content-Disposition",
                    "attachment;filename=\"" + URLEncoder.encode(doc.getName(), "utf-8") + "\"");
        } catch (final UnsupportedEncodingException e) {
            MongoFileServiceImpl.log.error("", e);
        }

        try {
            this.gridFSBucket.downloadToStream(fsFile.getObjectId(), response.getOutputStream());
        } catch (final IOException e) {
            MongoFileServiceImpl.log.error("", e);
        }
    }

    private FileDocument getByMd5(final String md5) {
        final Query query = new Query().addCriteria(Criteria.where("md5").is(md5));
        final FileDocument fileDocument = this.mongoTemplate.findOne(query, FileDocument.class, this.collectionName);
        return fileDocument;
    }

    public String uploadFileToGridFS(final InputStream in, final String fileName, final String contentType) {
        this.gridFsTemplate.store(in, fileName, contentType);
        return fileName;
    }

    private String createFilePath(final String suffix) {
        final StringBuilder id = new StringBuilder();
        id.append(String.valueOf(System.currentTimeMillis()))
                .append(String.valueOf((int) (Math.random() * 10000)))
                .append(".")
                .append(suffix);
        return id.toString();
    }

    @Override
    public void deleteByFsId(final String fsId) {
        final Query query = new Query().addCriteria(Criteria.where("fsId").is(fsId));
        final FileDocument fileDocument =
                this.mongoTemplate.findAndRemove(query, FileDocument.class, this.collectionName);
        System.out.println(fileDocument);
    }

}
